//
//  ProcessEncoder.m
//  HPZZTong
//
//  Created by WenJun on 14-7-14.
//  Copyright (c) 2014年 Handpay. All rights reserved.
//

#import "ProcessEncoder.h"
#import <objc/runtime.h>
//#import "CategoryUtil.h"
#import "SwipeInfo.h"

@interface POSProcess()
@property (readonly,nonatomic) NSString * plainMac;
@property (strong,nonatomic) NSString * plainPin;
@end


@interface PropertyItem : NSObject
@property (strong,nonatomic) NSString * propertyName;
@property (strong,nonatomic) NSString * ivarName;
@property (nonatomic) Ivar ivar;

- (id)getValueFromObj:(id)obj;
- (void)setValue:(id)value obj:(id)obj;
@end

@implementation PropertyItem

- (id)getValueFromObj:(id)obj
{
    return object_getIvar(obj, _ivar);
}

- (void)setValue:(id)value obj:(id)obj
{
    object_setIvar(obj, _ivar, value);
}

@end

@interface EncodePair : NSObject

@property (strong,nonatomic) PropertyItem * plainItem;

@property (strong,nonatomic) PropertyItem * encItem;

@property (nonatomic,getter = isEncoded) BOOL encoded;

@end

@implementation EncodePair

@end

@interface ProcessEncoder()

@property (weak,nonatomic) POSProcess *process;

@property (readonly,nonatomic) NSMutableArray * encodePairArray;

@property (readonly,nonatomic) NSArray * propertyArray;

@end

@implementation ProcessEncoder
{
    EncodePair * _currentPair;
}

- (NSString *)joinMac
{
    if (!_propertyArray)
    {
        _propertyArray = [ProcessEncoder propertyArrayFromClass:[_process class]];
    }
    NSMutableString * plainMac = [NSMutableString string];
    [self appendMacElement:@"time" mac:plainMac];
    [self appendMacElement:@"encMerchantID" mac:plainMac];
    [self appendMacElement:@"encTerminalID" mac:plainMac];
    [self appendMacElement:@"encOrderID" mac:plainMac];
    [self appendMacElement:@"encAmount" mac:plainMac];
    [self appendMacElement:@"encCurrency" mac:plainMac];
    [self appendMacElement:@"encAccount" mac:plainMac];
//    if (![_process icMode])
    {
        [self appendMacValue:[_process swipeInfo].encTrack2 mac:plainMac];
    }
    [self appendMacValue:[_process csn] mac:plainMac];
    [self appendMacElement:@"encAccount2" mac:plainMac];
    if ([_process icMode])
    {
        [self appendMacValue:[_process swipeInfo].icNumber mac:plainMac];
        [self appendMacValue:[_process swipeInfo].dcData mac:plainMac];
    }
    return [plainMac substringFromIndex:1];
}

- (void)appendMacElement:(NSString *)name mac:(NSMutableString *)mac
{
    PropertyItem * item = [self itemWithPropertyName:name];
    if (item)
    {
        [self appendMacValue:[item getValueFromObj:_process] mac:mac];
    }
}

- (void)appendMacValue:(NSString *)value mac:(NSMutableString *)mac
{
    if (value.length > 0)
    {
        [mac appendFormat:@" %@",value];
    }
}

- (id)initWithProcess:(id)process
{
    self = [super init];
    _process = process;
    return self;
}

//- (void)start
//{
//    
//    if (_process.icMode && [NSString isNilOrEmpty:_process.swipeInfo.dcData])
//    {
//        [self doEMV];
//    }
//    else
//    {
//        [self encyptKeyData];
//    }
//}
//
////IC卡EMV交易流程
//- (void)doEMV
//{
//    [_process.currentSwiper emv:_process complete:^(HPSwiperResult *swiperResult) {
//        if (swiperResult.isSuccess)
//        {
//            [self encryptPin];
//        }
//        else
//        {
//            [_process dealResult:swiperResult];
//        }
//    }];
//}
//
//- (void)encryptPin
//{
//    [_process.currentSwiper encryptPin:_process.plainPin factor:_process.factor complete:^(HPSwiperResult *swiperResult) {
//        if (swiperResult.isSuccess)
//        {
//            [_process setValue:swiperResult.value forKeyPath:@"encPin"];
//            [self encyptKeyData];
//        }
//        else
//        {
//            [_process dealResult:swiperResult];
//        }
//    }];
//}

- (void)encyptKeyData
{
    [self genEncodePairArray];
    [self beginEncodeKeyData];
}

- (void)beginEncodeKeyData
{
    if (_encodePairArray.count > 0)
    {
        _currentPair = [_encodePairArray lastObject];
    }
    else
    {
        [_process dealResult:[HPSwiperResult successResult:nil]];
        return;
    }
    NSString * plainValue = [_currentPair.plainItem getValueFromObj:_process];
    [[_process currentSwiper] encryptKeyData:plainValue factor:[_process factor] complete:^(HPSwiperResult *swiperResult) {
        if (swiperResult.isSuccess)
        {
            [_currentPair.encItem setValue:swiperResult.value obj:_process];
            [_encodePairArray removeLastObject];
            [self beginEncodeKeyData];
        }
        else
        {
            [_process dealResult:swiperResult];
        }
    }];
}

- (void)calMac
{
    if ([_process plainMac].length > 0)
    {
        [[_process currentSwiper] encryptMac:[_process plainMac] factor:[_process factor] complete:^(HPSwiperResult *swiperResult) {
            [[_process currentSwiper]stopSwiper];
            [_process dealResult:swiperResult];
        }];
    }
    else
    {
        [[_process currentSwiper]stopSwiper];
        [_process dealResult:[HPSwiperResult successResult:nil]];
    }
}

- (void)genEncodePairArray
{
    _encodePairArray = [NSMutableArray array];
    _propertyArray = [ProcessEncoder propertyArrayFromClass:[_process class]];
    for (PropertyItem * item in _propertyArray)
    {
        if ([item.propertyName hasPrefix:@"plain"] && [[item getValueFromObj:_process] length] > 0)
        {
            EncodePair * pair = [[EncodePair alloc] init];
            pair.plainItem = item;
            [_encodePairArray addObject:pair];
        }
    }
    for (EncodePair * pair in _encodePairArray)
    {
        NSString * encName = [pair.plainItem.propertyName stringByReplacingOccurrencesOfString:@"plain" withString:@"enc"];
        PropertyItem * encItem = [self itemWithPropertyName:encName];
        if (encItem)
        {
            pair.encItem = encItem;
            pair.encoded = [[encItem getValueFromObj:_process] length] > 0;
        }
    }
    for (NSInteger index = _encodePairArray.count - 1; index >= 0; index--)
    {
        EncodePair * pair = [_encodePairArray objectAtIndex:index];
        if (!pair.encItem || pair.isEncoded)
        {
            [_encodePairArray removeObject:pair];
        }
    }
}

- (PropertyItem *)itemWithPropertyName:(NSString *)name
{
    for (PropertyItem * item in _propertyArray)
    {
        if ([item.propertyName isEqualToString:name])
        {
            return item;
        }
    }
    return nil;
}

- (EncodePair *)pairWithEncItem:(PropertyItem *)encItem
{
    NSString * suffix = [encItem.propertyName substringFromIndex:3];
    for (EncodePair * pair in _encodePairArray)
    {
        if ([pair.encItem.propertyName hasSuffix:suffix])
        {
            return pair;
        }
    }
    return nil;
}

+ (NSArray *)propertyArrayFromClass:(Class)cls
{
    NSMutableArray * propertyArray = [NSMutableArray array];
    while (cls != [NSObject class])
    {
        unsigned int propertyCount = 0;
        objc_property_t * propertyList = class_copyPropertyList(cls, &propertyCount);
        for (int index = 0; index < propertyCount; index++)
        {
            objc_property_t property = propertyList[index];
            PropertyItem * codingItem = [self propertyItemFromProperty:property class:cls];
            [propertyArray addObject:codingItem];
        }
        cls = class_getSuperclass(cls);
        free(propertyList); // mcm new : fix memory leak
    }
    return propertyArray;
}

+ (PropertyItem *)propertyItemFromProperty:(objc_property_t)property class:(Class)cls
{
    unsigned int attrCount = 0;
    // mcm : look return comment on document: An array of property attributes. You must free the array with free().
    // here has a memory leak!
    objc_property_attribute_t * attrList = property_copyAttributeList(property,&attrCount);
    NSString * propertyName = [NSString stringWithUTF8String:property_getName(property)];
    PropertyItem * item = [[PropertyItem alloc] init];
    item.propertyName = propertyName;
    for (int index = 0; index < attrCount; index ++)
    {
        objc_property_attribute_t attr = attrList[index];
        if (strcmp(attr.name, "V") == 0)
        {
            item.ivar = class_getInstanceVariable(cls, attr.value);
            item.ivarName = [NSString stringWithUTF8String:ivar_getName(item.ivar)];
        }
    }
    // mcm new : fix memory leak
    free(attrList);
    return item;
}

@end
